package com.dpiinc.TechPage.repository
import androidx.lifecycle.MutableLiveData
import androidx.paging.ItemKeyedDataSource
import com.dpiinc.TechPage.AWLog
import com.dpiinc.TechPage.api.NoteListApi
import com.dpiinc.TechPage.api.asyncSubscribe
import com.dpiinc.TechPage.room.bean.NoteEntity
import com.dpiinc.TechPage.viewmodel.SearchViewModel
import java.util.concurrent.Executor

/**
 * SearchNoteDataSource
 * Created by X on 2018/7/28.
 */
class SearchNoteDataSource(
        private val noteListApi: NoteListApi,
        private val keyword: String,
        private val searchType: Int,
        private val retryExecutor: Executor)
    : ItemKeyedDataSource<String, NoteEntity>() {
    // keep a function reference for the retry event
    private var retry: (() -> Any)? = null

    val networkState = MutableLiveData<NetworkState>()

    val totalCount = MutableLiveData<Int>()

    val initialLoad = MutableLiveData<NetworkState>()
    fun retryAllFailed() {
        val prevRetry = retry
        retry = null
        prevRetry?.let {
            retryExecutor.execute {
                it.invoke()
            }
        }
    }

    override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<NoteEntity>) {
        // ignored, since we only ever append to our initial load
    }

    override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<NoteEntity>) {
        // set network value to loading.
        networkState.postValue(NetworkState.LOADING)
        // even though we are using async retrofit API here, we could also use sync
        // it is just different to show that the callback can be called async.
        val request = if (searchType == SearchViewModel.SEARCH_BY_CONTENT) {
            noteListApi.loadMoreByKeyword(
                    pageSize = params.requestedLoadSize,
                    keyword = keyword,
                    noteId = params.key)
        } else {
            noteListApi.loadMoreByLabel(
                    pageSize = params.requestedLoadSize,
                    labelId = keyword,
                    noteId = params.key)
        }
        request.doFinally {
            AWLog.e("doFinally")
        }.asyncSubscribe({ response ->
            if (response.isSuccess) {
                val items = response.result?.list ?: emptyList()
                // clear retry since last request succeeded
                retry = null
                networkState.postValue(NetworkState.LOADED)
                callback.onResult(items)
            } else {
                retry = {
                    loadAfter(params, callback)
                }
                networkState.postValue(
                        NetworkState.error("error code: ${response.errorCode}"))
            }
        }, {
            // keep a lambda for future retry
            retry = {
                loadAfter(params, callback)
            }
            // publish the error
            val error = NetworkState.error(it.message ?: "unknown error")
            networkState.postValue(error)
        })
    }

    override fun getKey(item: NoteEntity): String = item.noteId

    override fun loadInitial(
            params: LoadInitialParams<String>,
            callback: LoadInitialCallback<NoteEntity>) {
        val request = if (searchType == SearchViewModel.SEARCH_BY_CONTENT) {
            noteListApi.searchByKeyword(
                    pageSize = params.requestedLoadSize,
                    keyword = keyword
            )
        } else {
            noteListApi.searchByLabel(
                    pageSize = params.requestedLoadSize,
                    labelId = keyword
            )
        }
        // update network states.
        // we also provide an initial load state to the listeners so that the UI can know when the
        // very first list is loaded.
        networkState.postValue(NetworkState.LOADING)
        initialLoad.postValue(NetworkState.LOADING)

        // triggered by a refresh, we better execute sync
        request.subscribe({ response ->
            if (response.isSuccess) {
                val items = response.result?.list ?: emptyList()
                retry = null
                totalCount.postValue(response.result.totalCount)
                networkState.postValue(NetworkState.LOADED)
                initialLoad.postValue(NetworkState.LOADED)
                callback.onResult(items)
            } else {
                retry = {
                    loadInitial(params, callback)
                }
                val error = NetworkState.error(response.errorCode.toString())
                networkState.postValue(error)
                initialLoad.postValue(error)
            }
        }, {
            retry = {
                loadInitial(params, callback)
            }
            val error = NetworkState.error(it.message ?: "unknown error")
            networkState.postValue(error)
            initialLoad.postValue(error)
        })
        //这里异步请求会有问题
//        request.doFinally {
//            AWLog.e("doFinally")
//        }.asyncSubscribe({ response ->
//            val items = response.result?.list ?: emptyList()
//            retry = null
//            networkState.postValue(NetworkState.LOADED)
//            initialLoad.postValue(NetworkState.LOADED)
//            callback.onResult(items)
//        }, {
//            retry = {
//                loadInitial(params, callback)
//            }
//            val error = NetworkState.error(it.message ?: "unknown error")
//            networkState.postValue(error)
//            initialLoad.postValue(error)
//        })
    }
}